package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.gateway.constant.RedisConst;
import com.atguigu.gmall.model.user.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

/**
 * @Author BrookPeng
 * @Date 2020/12/2 1:22
 * @Version 1.0
 * <p>
 * 全局过滤器:判断用户是否登陆
 *
 * GlobalFilter: 默认有9大过滤器 SpringCloudGateWay
 *
 *  10大过滤器 = 9大过滤器 + 自定义过滤器
 */
@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {

    @Value("${auth.url}")
    private String[] authUrl;

    @Value("${login.url}")
    private String loginUrl;

    @Autowired
    private RedisTemplate redisTemplate;

    // 页面 混合请求 同步请求 应该从Cookie中获取token 连接Redis中获取用户信息
    // 页面 混合请求 异步请求 应该从请求头中获取token 连接Redis中获取用户信息


    // 全局请求过滤的方法
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 路径匹配
        AntPathMatcher antPathMatcher = new AntPathMatcher();

        // 判断是否登陆 token
        ServerHttpRequest request = exchange.getRequest(); // 非http请求的request和response
        ServerHttpResponse response = exchange.getResponse();
        // list.html
        String path = request.getURI().getPath();
        System.out.println("request.getURI().getPath() = " + path);
        // http://list.gmall.com/list.html?keyword=........
        System.out.println("request.getURI().getRawSchemeSpecificPart() = " + request.getURI().getRawSchemeSpecificPart());

        // 获取token
        String userId = getToken(request);

        // 1:访问微服务时 商品为服务 查询微服务 订单微服务 详情微服务等 异步请求
        // 判断当前路径是否需要登陆 ../auth/..
        if (antPathMatcher.match("/**/auth/**/", path)) {
            // 必须登陆 判断是否登陆
            if (userId == null) {
                // 2:未登录 直接返回结果 ( result 设置 LOGIN_AUTH(208, "未登陆"))
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
            // 1:登陆了 正常转发
        }
        // 2:内部资源 网关不给访问
        if (antPathMatcher.match("/**/inner/**/", path)) {
            // 直接返回 ( result 设置 PERMISSION(209, "没有权限"))
            return out(response, ResultCodeEnum.PERMISSION);
        }

        // 3:访问页面微服务时 同步请求
        // 首页/
        // 搜索list.html
        // 商品详情/234567.html
        // 购物车 /cart.html
        // 结算 /trade.html
        // 订单页面 /order.html
        // 支付页面 /pay.html
        for (String s : authUrl) {
            if (s.equals(path)) {
                // 判断是否登陆
                // 未登录 重定向到登录页面
                if (null == userId) {

                    // http://passport.gmall.com/login.html?originUrl=http%3A%2F%2Fwww.gmall.com%2F
                    // 原来是response.sendRedirect,前提是response是http的response
                    try {
                        // 现在head中设置重定向地址
                        response.getHeaders().add(HttpHeaders.LOCATION, loginUrl
                                + URLEncoder.encode(request.getURI().getRawSchemeSpecificPart(),
                                "utf-8"));
                        // 设置response支持重定向
                        // 状态码 200正常 404找不到资源 400参数不正确 500后台发生异常 303重定向
                        response.setStatusCode(HttpStatus.SEE_OTHER);
                        Mono<Void> voidMono = response.setComplete();
                        return voidMono;
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        // 首页
        // 购物车页面
        // 如果已经登陆了userId不为null
        if (userId != null) {
            // 传递用户ID给后面需要转发的微服务 使用请求头携带
            // request.getHeaders().add("userId", userId); 不能这样用，以为此时request是有浏览器发出，链接依然存在，不允许更改其中的内容
            request.mutate().header("userId", userId);
        }

        String tempUserId = getTempUserId(request);
        if (tempUserId != null) {
            // 未登录的情况下，使用临时id
            request.mutate().header("userTempId", tempUserId);
        }

        // 转发后面的微服务
        return chain.filter(exchange);
    }

    // 获取临时用户ID
    private String getTempUserId(ServerHttpRequest request) {

        // 1: 从请求头获取token信息获取ID
        String userTempId = request.getHeaders().getFirst("userTempId");
        if (userTempId == null) {
            HttpCookie userTempIdFromCookie = request.getCookies().getFirst("userTempId");
            if (userTempIdFromCookie != null) {
                userTempId = userTempIdFromCookie.getValue();
            }
        }
        return userTempId;
    }

    // 不许访问时候的响应结果
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {

        Result<Object> build = Result.build(null, resultCodeEnum);
        String string = JSONObject.toJSONString(build);
        DataBufferFactory dataBufferFactory = response.bufferFactory();
        DataBuffer wrap = dataBufferFactory.wrap(string.getBytes());
        Mono<Void> voidMono = response.writeWith(Mono.just(wrap));
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf=8");
        return voidMono;
    }

    // 获取用户ID
    private String getToken(ServerHttpRequest request) {

        // 1: 从请求头获取token信息获取ID
        String token = request.getHeaders().getFirst("token");
        if (null == token) {
            // 3: 没有 从Cookie中获取
            HttpCookie tokenFromCookie = request.getCookies().getFirst("token");
            if (tokenFromCookie != null) {
                String token1 = tokenFromCookie.getValue();
                if (token1 != null) {
                    // 4: 有 直接使用
                    if (redisTemplate.hasKey(RedisConst.USER_LOGIN_KEY_PREFIX + token1)) {
                        UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(RedisConst.USER_LOGIN_KEY_PREFIX + token1);
                        return userInfo.getId().toString();
                    }
                }
            }
        } else {
            // 2: 有 直接使用
            if (redisTemplate.hasKey(RedisConst.USER_LOGIN_KEY_PREFIX + token)) {
                UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(RedisConst.USER_LOGIN_KEY_PREFIX + token);
                return userInfo.getId().toString();
            }
        }

        // 5: 没有 返回Null
        return null;
    }

    // 1:先获取token 2:解析token jwt Cookie 时效 关闭浏览器即销毁

    @Override
    public int getOrder() {
        return 0;
    }
}
